The goal of this project is to investigate how partnerships involving
multiple top-tier players in the NBA impacts various performance
measures and team outcomes. Among the research questions we would like
to explore are the following:
[ INSERT RESEARCH QUESTIONS HERE ]
To be able to investigate, we need to pull data from multiple NBA
seasons. The script below provides code to create functions that pull
traditional stats for every player for a given user-defined season.
Load necessary libraries
library(rvest)
library(dplyr)
library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats 1.0.0 ✔ readr 2.1.5
✔ ggplot2 3.5.1 ✔ stringr 1.5.1
✔ lubridate 1.9.3 ✔ tibble 3.2.1
✔ purrr 1.0.2 ✔ tidyr 1.3.1
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ readr::guess_encoding() masks rvest::guess_encoding()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
Function to get NBA roster for a specified year
get_nba_roster <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
# Read the HTML content from the URL
webpage <- read_html(url)
# Extract the table containing the player statistics
roster_table <- webpage %>%
html_node("table#per_game_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
roster_table <- roster_table %>%
filter(Player != "Player")
return(roster_table)
}
Example usage
year <- 2018 # Specify the year
nba_roster <- get_nba_roster(year)
#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)
NA
#Summary statistics
position_roster<-filter(nba_roster,Pos!="PG" )
position_roster
library(plotly)
# Assuming 'nba_roster' is your data frame
input <- nba_roster[, c('MP', 'PTS', 'Player','Pos')]
# Create the plotly scatter plot
fig <- plot_ly(input, x = ~MP, y = ~PTS, type = 'scatter', mode = 'markers',
text = ~Player, # This adds player names on hover
hoverinfo = 'text', # Ensures that only player names appear on hover
color = ~Pos, # Colors points based on position
marker = list(size = 10))
# Set the plot title and axis labels
fig <- fig %>% layout(title = "Minutes Played vs Points Scored",
xaxis = list(title = "Minutes Played", range = c(0, 48)),
yaxis = list(title = "Points", range = c(0, 35)))
# Show the plot
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
#Data Visualization for Minutes Played vs Points Scored
input <- nba_roster[, c('MP', 'PTS')]
print(head(input))
# Get the input values.
input <- nba_roster[, c('MP', 'PTS')]
# Plot the chart for cars with
# weight between 1.5 to 4 and
# mileage between 10 and 25.
plot(x = input$MP, y = input$PTS,
xlab = "Minutes Played",
ylab = "Points",
xlim = c(0.0, 48),
ylim = c(0.0, 35),
main = "Minutes Played vs Points Scored"
)

#Data Visualization for Field Goals Attempled vs Field Goals Made
input_2 <- nba_roster[, c('FGA', 'FG')]
print(head(input_2))
# Get the input values.
input_2 <- nba_roster[, c('FGA', 'FG')]
# Plot the chart for players with
# field goal attempts between 0.0 to 25.0 and
b_FG<-max(input_2$FG,na.rm=T)
b_FGA<-max(input_2$FGA,na.rm=T)
# Field Goal Made between 0.0 and 25.0
plot(x = input_2$FGA, y = input_2$FG,
xlab = "Field Goal Attempts",
ylab = "Field Goal Made",
xlim = c(0.0, b_FGA),
ylim = c(0.0, b_FG),
main = "Field Goal Attempt vs Field Goal Made"
)

NA
NA
# Create the data for the chart
A <- c(nba_roster$PTS)
B <- c("PF", "PG", "SF", "C", "SG")
# Plot the bar chart
ggplot(nba_roster, aes(x=Pos, fill=PTS))+
geom_bar()+
theme_classic(16)+
xlab("Position")+
ylab("Points")
Warning: The following aesthetics were dropped during statistical transformation: fill.
ℹ This can happen when ggplot fails to infer the correct grouping structure in the data.
ℹ Did you forget to specify a `group` aesthetic or to convert a numerical variable into a factor?

ASSIGNMENT 1: Is the data “clean”? Are there any
missing values to be accounted for/addressed? If there are any data
quality issues,
- a. propose a method to resolve them
Initial thought to change the default character to double given that
we have fractioned values. i think the columns should be changed from ”
chararcter” to “double”
b. justify the validity of your approach removing
observations with missing data from the dataset, using the function
“na.omit” which will remove rows with missing values from our
dataset
c. implement your proposed changes
For players who are missing data
nba_roster<-na.omit(nba_roster)
nba_roster
# Convert specific columns from character to double
# Convert all character columns to double
nba_roster %>%
mutate(across(G:PTS, as.numeric))
NA
NA
NA
To determine whether a player is “top tier” and should be considered
a part of a “Big 3” lineup, other authors have transformed traditional
stats to create metrics such as
PRA = POINTS + REBOUNDS + ASSISTS
We will consider advanced statistics such as PLAYER EFFIFIENCY
RATING:
PER = (PTS + REB + AST + STL + BLK − Missed FG − Missed FT - TO)
/GP
In particular, Value over Replacement (VORP) seems to do a solid job
of identifying the best players in the league.
The script below provide code to create functions that pull advanced
stats for every player for a given user-defined season.
# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
# Read the HTML content from the URL
webpage <- read_html(url)
# Extract the table containing the advanced player statistics
advanced_stats_table <- webpage %>%
html_node("table#advanced_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
# advanced_stats_table <- advanced_stats_table %>%
# filter(Player != "Player")
return(advanced_stats_table)
}
# Example usage
year <- 2018 # Specify the year
nba_advanced_stats <- get_nba_advanced_stats(year)
# Print the first few rows of the advanced stats
head(nba_advanced_stats)
NA
NA
ASSIGNMENT 2: Is the advanced data “clean”? Are
there any missing values to be accounted for/addressed? If there are any
data quality issues,
a. propose a method to resolve them
b. justify the validity of your approach
c. implement your proposed changes
cleaning similar to first one
The script below provide code to clean out the quality issues
presented in the dataframe
#1 We want to order the athletes name to alphabetical order to clean out the filler headers present
newdataframe<- dataframe[order(dataframe$Player)]
Error: object 'dataframe' not found
#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats <- nba_advanced_stats[order(nba_advanced_stats$Player),]
# remove filler rows that had been previously used as headers on webpage
AO_nba_advanced_stats<- AO_nba_advanced_stats[-c(502:526), ]
#remove na from dataframe
AO_nba_advanced_stats %>%
select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-20]
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-24]
#change range of cloumns <dbl> from <chr>
AO_nba_advanced_stats %>%
mutate(across(G:VORP, as.numeric))
Warning: There were 22 warnings in `mutate()`.
The first warning was:
ℹ In argument: `across(G:VORP, as.numeric)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 21 remaining warnings.
ASSIGNMENT 3: Merge the cleaned up datasets to
create one new data frame with the traditional and advanced
stats.
#how to merge two files into one new data frame
nba_merge<-merge(nba_roster, AO_nba_advanced_stats, by = c("Rk", "Player", "Pos","Age", "Tm","G"))#, by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
Error in fix.by(by.x, x) : 'by' must specify a uniquely valid column
ASSIGNMENT 4: Make a function with argument
year that outputs one dataframe with the merged traditional
and advanced data.
combined_nba_stats<-function(year){
get_nba_roster2 <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
# Read the HTML content from the URL
webpage <- read_html(url)
# Extract the table containing the player statistics
roster_table <- webpage %>%
html_node("table#per_game_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
roster_table <- roster_table %>%
filter(Player != "Player")
return(roster_table)
}
year <- 2023 # Specify the year
nba_roster2 <- get_nba_roster2(year)
#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)
#take out the N/A
nba_roster2<-na.omit(nba_roster2)
# Convert specific columns from character to double
nba_roster2 %>%
mutate(across(G:PTS, as.numeric))
#ADVANCED STATS
# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
# Read the HTML content from the URL
webpage <- read_html(url)
# Extract the table containing the advanced player statistics
advanced_stats_table <- webpage %>%
html_node("table#advanced_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
# advanced_stats_table <- advanced_stats_table %>%
# filter(Player != "Player")
return(advanced_stats_table)
}
# Example usage
year <- 2023 # Specify the year
nba_advanced_stats2<- get_nba_advanced_stats(year)
# Print the first few rows of the advanced stats
head(nba_advanced_stats2)
#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats2<- nba_advanced_stats2[order(nba_advanced_stats2$Player),]
#remove na from dataframe
AO_nba_advanced_stats2 %>%
select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-20]
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-24]
# remove filler rows that had been previously used as headers on webpage
AO_nba_advanced_stats2 <- AO_nba_advanced_stats2[AO_nba_advanced_stats2$Player != "Player",]
AO_nba_advanced_stats2$Player <- factor(AO_nba_advanced_stats2$Player)
#change range of cloumns <dbl> from <chr>
AO_nba_advanced_stats2 %>%
mutate(across(G:VORP, as.numeric))
nba_merge<-merge(nba_roster2, AO_nba_advanced_stats2, by.x = c("Rk", "Player", "Pos","Age", "Tm","G"), by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
}
ASSIGNMENT 5: Make this file more visually
appealng, with headers, bullet points, sections and subsections as you
see fit. You may consider migrating over to Quarto for this
reason.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGludmVzdGlnYXRlIGhvdyBwYXJ0bmVyc2hpcHMgaW52b2x2aW5nIG11bHRpcGxlIHRvcC10aWVyIHBsYXllcnMgaW4gdGhlIE5CQSBpbXBhY3RzIHZhcmlvdXMgcGVyZm9ybWFuY2UgbWVhc3VyZXMgYW5kIHRlYW0gb3V0Y29tZXMuIEFtb25nIHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgd2Ugd291bGQgbGlrZSB0byBleHBsb3JlIGFyZSB0aGUgZm9sbG93aW5nOgoKKlsgSU5TRVJUIFJFU0VBUkNIIFFVRVNUSU9OUyBIRVJFIF0qCgpUbyBiZSBhYmxlIHRvIGludmVzdGlnYXRlLCB3ZSBuZWVkIHRvIHB1bGwgZGF0YSBmcm9tIG11bHRpcGxlIE5CQSBzZWFzb25zLiBUaGUgc2NyaXB0IGJlbG93IHByb3ZpZGVzIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgdHJhZGl0aW9uYWwgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgoKTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpGdW5jdGlvbiB0byBnZXQgTkJBIHJvc3RlciBmb3IgYSBzcGVjaWZpZWQgeWVhcgpgYGB7cn0KZ2V0X25iYV9yb3N0ZXIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KYGBgCgpFeGFtcGxlIHVzYWdlCmBgYHtyfQp5ZWFyIDwtIDIwMTggICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyIDwtIGdldF9uYmFfcm9zdGVyKHllYXIpCgojUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByb3N0ZXIKaGVhZChuYmFfcm9zdGVyKQp0YWlsKG5iYV9yb3N0ZXIpCgpgYGAKYGBge3J9CiNTdW1tYXJ5IHN0YXRpc3RpY3MKCnBvc2l0aW9uX3Jvc3RlcjwtZmlsdGVyKG5iYV9yb3N0ZXIsUG9zIT0iUEciICkKcG9zaXRpb25fcm9zdGVyCmBgYAoKCgpgYGB7cn0KCmxpYnJhcnkocGxvdGx5KQoKIyBBc3N1bWluZyAnbmJhX3Jvc3RlcicgaXMgeW91ciBkYXRhIGZyYW1lCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnLCAnUGxheWVyJywnUG9zJyldCgojIENyZWF0ZSB0aGUgcGxvdGx5IHNjYXR0ZXIgcGxvdApmaWcgPC0gcGxvdF9seShpbnB1dCwgeCA9IH5NUCwgeSA9IH5QVFMsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbWFya2VycycsCiAgICAgICAgICAgICAgIHRleHQgPSB+UGxheWVyLCAgIyBUaGlzIGFkZHMgcGxheWVyIG5hbWVzIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGhvdmVyaW5mbyA9ICd0ZXh0JywgIyBFbnN1cmVzIHRoYXQgb25seSBwbGF5ZXIgbmFtZXMgYXBwZWFyIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGNvbG9yID0gflBvcywgICMgQ29sb3JzIHBvaW50cyBiYXNlZCBvbiBwb3NpdGlvbgogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCkpCgojIFNldCB0aGUgcGxvdCB0aXRsZSBhbmQgYXhpcyBsYWJlbHMKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIHZzIFBvaW50cyBTY29yZWQiLAogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIiwgcmFuZ2UgPSBjKDAsIDQ4KSksCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUG9pbnRzIiwgcmFuZ2UgPSBjKDAsIDM1KSkpCgojIFNob3cgdGhlIHBsb3QKZmlnCgoKYGBgCmBgYHtyfQojRGF0YSBWaXN1YWxpemF0aW9uIGZvciBNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnKV0KcHJpbnQoaGVhZChpbnB1dCkpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dCA8LSBuYmFfcm9zdGVyWywgYygnTVAnLCAnUFRTJyldCgojIFBsb3QgdGhlIGNoYXJ0IGZvciBjYXJzIHdpdGgKIyB3ZWlnaHQgYmV0d2VlbiAxLjUgdG8gNCBhbmQKIyBtaWxlYWdlIGJldHdlZW4gMTAgYW5kIDI1LgpwbG90KHggPSBpbnB1dCRNUCwgeSA9IGlucHV0JFBUUywKCXhsYWIgPSAiTWludXRlcyBQbGF5ZWQiLAoJeWxhYiA9ICJQb2ludHMiLAoJeGxpbSA9IGMoMC4wLCA0OCksCgl5bGltID0gYygwLjAsIDM1KSwJIAoJbWFpbiA9ICJNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkIgopCgpgYGAKCgpgYGB7cn0KI0RhdGEgVmlzdWFsaXphdGlvbiBmb3IgRmllbGQgR29hbHMgQXR0ZW1wbGVkIHZzIEZpZWxkIEdvYWxzIE1hZGUKaW5wdXRfMiA8LSBuYmFfcm9zdGVyWywgYygnRkdBJywgJ0ZHJyldCnByaW50KGhlYWQoaW5wdXRfMikpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dF8yIDwtIG5iYV9yb3N0ZXJbLCBjKCdGR0EnLCAnRkcnKV0KCiMgUGxvdCB0aGUgY2hhcnQgZm9yIHBsYXllcnMgd2l0aAojIGZpZWxkIGdvYWwgYXR0ZW1wdHMgYmV0d2VlbiAwLjAgdG8gMjUuMCBhbmQKYl9GRzwtbWF4KGlucHV0XzIkRkcsbmEucm09VCkKYl9GR0E8LW1heChpbnB1dF8yJEZHQSxuYS5ybT1UKQojIEZpZWxkIEdvYWwgTWFkZSBiZXR3ZWVuIDAuMCBhbmQgMjUuMApwbG90KHggPSBpbnB1dF8yJEZHQSwgeSA9IGlucHV0XzIkRkcsCgl4bGFiID0gIkZpZWxkIEdvYWwgQXR0ZW1wdHMiLAoJeWxhYiA9ICJGaWVsZCBHb2FsIE1hZGUiLAoJeGxpbSA9IGMoMC4wLCBiX0ZHQSksCgl5bGltID0gYygwLjAsIGJfRkcpLAkgCgltYWluID0gIkZpZWxkIEdvYWwgQXR0ZW1wdCB2cyBGaWVsZCBHb2FsIE1hZGUiCikKCgpgYGAKCgoKYGBge3J9CiMgQ3JlYXRlIHRoZSBkYXRhIGZvciB0aGUgY2hhcnQKQSA8LSBjKG5iYV9yb3N0ZXIkUFRTKQpCIDwtIGMoIlBGIiwgIlBHIiwgIlNGIiwgIkMiLCAiU0ciKQoKIyBQbG90IHRoZSBiYXIgY2hhcnQKZ2dwbG90KG5iYV9yb3N0ZXIsIGFlcyh4PVBvcywgZmlsbD1QVFMpKSsgIAoKZ2VvbV9iYXIoKSsgIAoKdGhlbWVfY2xhc3NpYygxNikrICAKCnhsYWIoIlBvc2l0aW9uIikrICAKCnlsYWIoIlBvaW50cyIpIAoKYGBgCgoKKipBU1NJR05NRU5UIDE6KiogKklzIHRoZSBkYXRhICJjbGVhbiI/IEFyZSB0aGVyZSBhbnkgbWlzc2luZyB2YWx1ZXMgdG8gYmUgYWNjb3VudGVkIGZvci9hZGRyZXNzZWQ/IElmIHRoZXJlIGFyZSBhbnkgZGF0YSBxdWFsaXR5IGlzc3VlcywqCgogLSAqYS4gcHJvcG9zZSBhIG1ldGhvZCB0byByZXNvbHZlIHRoZW0qCiAgICAgICAKSW5pdGlhbCB0aG91Z2h0IHRvIGNoYW5nZSB0aGUgZGVmYXVsdCBjaGFyYWN0ZXIgdG8gZG91YmxlIGdpdmVuIHRoYXQgd2UgaGF2ZSBmcmFjdGlvbmVkIHZhbHVlcy4gaSB0aGluayB0aGUgY29sdW1ucyBzaG91bGQgYmUgY2hhbmdlZCBmcm9tICIgY2hhcmFyY3RlciIgdG8gImRvdWJsZSIgCgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCnJlbW92aW5nIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgZGF0YSBmcm9tIHRoZSBkYXRhc2V0LCB1c2luZyB0aGUgZnVuY3Rpb24gIm5hLm9taXQiIHdoaWNoIHdpbGwgcmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBmcm9tIG91ciBkYXRhc2V0CgoKIC0gKmMuIGltcGxlbWVudCB5b3VyIHByb3Bvc2VkIGNoYW5nZXMqCgoKRm9yIHBsYXllcnMgd2hvIGFyZSBtaXNzaW5nIGRhdGEKYGBge3J9Cm5iYV9yb3N0ZXI8LW5hLm9taXQobmJhX3Jvc3RlcikKCm5iYV9yb3N0ZXIKCgoKCiMgQ29udmVydCBzcGVjaWZpYyBjb2x1bW5zIGZyb20gY2hhcmFjdGVyIHRvIGRvdWJsZQojIENvbnZlcnQgYWxsIGNoYXJhY3RlciBjb2x1bW5zIHRvIGRvdWJsZQpuYmFfcm9zdGVyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6UFRTLCBhcy5udW1lcmljKSkKCgoKYGBgCgpUbyBkZXRlcm1pbmUgd2hldGhlciBhIHBsYXllciBpcyAidG9wIHRpZXIiIGFuZCBzaG91bGQgYmUgY29uc2lkZXJlZCBhIHBhcnQgb2YgYSAiQmlnIDMiIGxpbmV1cCwgb3RoZXIgYXV0aG9ycyBoYXZlIHRyYW5zZm9ybWVkIHRyYWRpdGlvbmFsIHN0YXRzIHRvIGNyZWF0ZSBtZXRyaWNzIHN1Y2ggYXMKClBSQSA9IFBPSU5UUyArIFJFQk9VTkRTICsgQVNTSVNUUyAKCldlIHdpbGwgY29uc2lkZXIgYWR2YW5jZWQgc3RhdGlzdGljcyBzdWNoIGFzIFBMQVlFUiBFRkZJRklFTkNZIFJBVElORzoKClBFUiA9IChQVFMgKyBSRUIgKyBBU1QgKyBTVEwgKyBCTEsg4oiSIE1pc3NlZCBGRyDiiJIgTWlzc2VkIEZUIC0gVE8pIC9HUAoKSW4gcGFydGljdWxhciwgVmFsdWUgb3ZlciBSZXBsYWNlbWVudCAoVk9SUCkgc2VlbXMgdG8gZG8gYSBzb2xpZCBqb2Igb2YgaWRlbnRpZnlpbmcgdGhlIGJlc3QgcGxheWVycyBpbiB0aGUgbGVhZ3VlLgoKVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgYWR2YW5jZWQgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgpgYGB7cn0KCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDE4ICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0cykKCgpgYGAKCioqQVNTSUdOTUVOVCAyOioqICpJcyB0aGUgYWR2YW5jZWQgZGF0YSAiY2xlYW4iPyBBcmUgdGhlcmUgYW55IG1pc3NpbmcgdmFsdWVzIHRvIGJlIGFjY291bnRlZCBmb3IvYWRkcmVzc2VkPyBJZiB0aGVyZSBhcmUgYW55IGRhdGEgcXVhbGl0eSBpc3N1ZXMsKgoKIC0gKmEuIHByb3Bvc2UgYSBtZXRob2QgdG8gcmVzb2x2ZSB0aGVtKgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCgogLSAqYy4gaW1wbGVtZW50IHlvdXIgcHJvcG9zZWQgY2hhbmdlcyoKIAogY2xlYW5pbmcgc2ltaWxhciB0byBmaXJzdCBvbmUgCiAKIAogVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY2xlYW4gb3V0IHRoZSBxdWFsaXR5IGlzc3VlcyBwcmVzZW50ZWQgaW4gdGhlIGRhdGFmcmFtZQogCiAKIApgYGB7cn0KIzEgV2Ugd2FudCB0byBvcmRlciB0aGUgYXRobGV0ZXMgbmFtZSB0byBhbHBoYWJldGljYWwgb3JkZXIgdG8gY2xlYW4gb3V0IHRoZSBmaWxsZXIgaGVhZGVycyBwcmVzZW50CgpuZXdkYXRhZnJhbWU8LSBkYXRhZnJhbWVbb3JkZXIoZGF0YWZyYW1lJFBsYXllcildCgojMiBOb3cgd2Ugd2FudCB0byByZW1vdmUgdGhlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gdXNlZCBhcyBoZWFkZXJzIG9uIHRoZSB3ZWJwYWdlCgpuZXdkYXRhLmZyYW1lPC1kYXRhZnJhbWVbLWMoNTAyOjUyNiksIF0KCiMzIG5vdyB3ZSB3YW50IHRvIHJlbW92ZSBhbGwgdGhlIE4vQXMgZnJvbSB0aGUgZGF0YXNldApkYXRhZnJhbWUgJT4lIAogIHNlbGVjdCh3aGVyZSh+IWFsbChpcy5uYSguKSkpKQpgYGAKIAogCiAKYGBge3J9Cgojd2FudCB0byBvcmRlciBieSBhbHBoYWJldGljIG5hbWUgdG8gbWFrZSBjbGVhbmluZyBvdXQgdGhlIGZpbGxlciBoZWFkZXJzIGZyb20gdGhlIGRhdGFzZXQKQU9fbmJhX2FkdmFuY2VkX3N0YXRzIDwtIG5iYV9hZHZhbmNlZF9zdGF0c1tvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMkUGxheWVyKSxdCgoKCiMgcmVtb3ZlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gcHJldmlvdXNseSB1c2VkIGFzIGhlYWRlcnMgb24gd2VicGFnZQpBT19uYmFfYWR2YW5jZWRfc3RhdHM8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHNbLWMoNTAyOjUyNiksIF0KCgojcmVtb3ZlIG5hIGZyb20gZGF0YWZyYW1lCkFPX25iYV9hZHZhbmNlZF9zdGF0cyAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yMF0KQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yNF0KCgojY2hhbmdlIHJhbmdlIG9mIGNsb3VtbnMgPGRibD4gZnJvbSA8Y2hyPgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCgoKCgpgYGAKCgoqKkFTU0lHTk1FTlQgMzoqKiAqTWVyZ2UgdGhlIGNsZWFuZWQgdXAgZGF0YXNldHMgdG8gY3JlYXRlIG9uZSBuZXcgZGF0YSBmcmFtZSB3aXRoIHRoZSB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgc3RhdHMuKgoKCgpgYGB7cn0KI2hvdyB0byBtZXJnZSB0d28gZmlsZXMgaW50byBvbmUgbmV3IGRhdGEgZnJhbWUKbmJhX21lcmdlPC1tZXJnZShuYmFfcm9zdGVyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMsIGJ5LnggPSBjKCJSayIsICJQbGF5ZXIiLCAiUG9zIiwiQWdlIiwgIlRtIiwiRyIpLCBieS55ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSAsIGFsbC54ID0gVFJVRSwgYWxsLnkgPSBUUlVFKQoKCmhlYWQobmJhX21lcmdlKQpgYGAKCgoqKkFTU0lHTk1FTlQgNDoqKiAqTWFrZSBhIGZ1bmN0aW9uIHdpdGggYXJndW1lbnQgYHllYXJgIHRoYXQgb3V0cHV0cyBvbmUgZGF0YWZyYW1lIHdpdGggdGhlIG1lcmdlZCB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgZGF0YS4qIAoKYGBge3J9Cgpjb21iaW5lZF9uYmFfc3RhdHM8LWZ1bmN0aW9uKHllYXIpewpnZXRfbmJhX3Jvc3RlcjIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KICAKICB5ZWFyIDwtIDIwMjMgICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyMiA8LSBnZXRfbmJhX3Jvc3RlcjIoeWVhcikKCiNQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIHJvc3RlcgpoZWFkKG5iYV9yb3N0ZXIpCnRhaWwobmJhX3Jvc3RlcikKCgojdGFrZSBvdXQgdGhlIE4vQSAKbmJhX3Jvc3RlcjI8LW5hLm9taXQobmJhX3Jvc3RlcjIpCgoKIyBDb252ZXJ0IHNwZWNpZmljIGNvbHVtbnMgZnJvbSBjaGFyYWN0ZXIgdG8gZG91YmxlCgpuYmFfcm9zdGVyMiAlPiUKICAgbXV0YXRlKGFjcm9zcyhHOlBUUywgYXMubnVtZXJpYykpCgojQURWQU5DRUQgU1RBVFMKCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDIzICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzMjwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0czIpCgoKCiN3YW50IHRvIG9yZGVyIGJ5IGFscGhhYmV0aWMgbmFtZSB0byBtYWtlIGNsZWFuaW5nIG91dCB0aGUgZmlsbGVyIGhlYWRlcnMgZnJvbSB0aGUgZGF0YXNldApBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC0gbmJhX2FkdmFuY2VkX3N0YXRzMltvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllciksXQoKCgoKCiNyZW1vdmUgbmEgZnJvbSBkYXRhZnJhbWUKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMjwtQU9fbmJhX2FkdmFuY2VkX3N0YXRzMlssLTIwXQpBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC1BT19uYmFfYWR2YW5jZWRfc3RhdHMyWywtMjRdCgoKIyByZW1vdmUgZmlsbGVyIHJvd3MgdGhhdCBoYWQgYmVlbiBwcmV2aW91c2x5IHVzZWQgYXMgaGVhZGVycyBvbiB3ZWJwYWdlCgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiA8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHMyW0FPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyICE9ICJQbGF5ZXIiLF0KCkFPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyIDwtIGZhY3RvcihBT19uYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllcikKCgoKCiNjaGFuZ2UgcmFuZ2Ugb2YgY2xvdW1ucyA8ZGJsPiBmcm9tIDxjaHI+CgpBT19uYmFfYWR2YW5jZWRfc3RhdHMyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCiAgIAogICBuYmFfbWVyZ2U8LW1lcmdlKG5iYV9yb3N0ZXIyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMyLCBieS54ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSwgYnkueSA9IGMoIlJrIiwgIlBsYXllciIsICJQb3MiLCJBZ2UiLCAiVG0iLCJHIikgLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gVFJVRSkKICAgCn0KCgoKYGBgCgoKKipBU1NJR05NRU5UIDU6KiogKk1ha2UgdGhpcyBmaWxlIG1vcmUgdmlzdWFsbHkgYXBwZWFsbmcsIHdpdGggaGVhZGVycywgYnVsbGV0IHBvaW50cywgc2VjdGlvbnMgYW5kIHN1YnNlY3Rpb25zIGFzIHlvdSBzZWUgZml0LiBZb3UgbWF5IGNvbnNpZGVyIG1pZ3JhdGluZyBvdmVyIHRvIFF1YXJ0byBmb3IgdGhpcyByZWFzb24uKgoKCg==